// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include "addrspace.h"
#include "../network/post.h"
#include <stdio.h>
#include <iostream>
#include <cmath>
#include <time.h>

using namespace std;
//-------Tools used in Project2 Part2-------------------
extern Lock *ProcessTableLock;
extern ProcessTable *procTable;
extern Lock *PhysMapLock;
extern BitMap* PhysMap;
extern OpenFile *swapFile;
extern BitMap *swapMap;
extern Lock *swapLock;

extern List *ppnFIFOList;	//for Project3 part2
extern int pageReplaceType;
//-----------------end of tools-------------------------
int copyin(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes from the current thread's virtual address vaddr.
    // Return the number of bytes so read, or -1 if an error occors.
    // Errors can generally mean a bad virtual address was passed in.
    bool result;
    int n=0;			// The number of bytes copied in
    int *paddr = new int;

    while ( n >= 0 && n < len) {
      result = machine->ReadMem( vaddr, 1, paddr );
      while(!result) // FALL 09 CHANGES
	  {
   			result = machine->ReadMem( vaddr, 1, paddr ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
	  }	
      
      buf[n++] = *paddr;
     
      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    delete paddr;
    return len;
}

int copyout(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes to the current thread's virtual address vaddr.
    // Return the number of bytes so written, or -1 if an error
    // occors.  Errors can generally mean a bad virtual address was
    // passed in.
    bool result;
    int n=0;			// The number of bytes copied in

    while ( n >= 0 && n < len) {
      // Note that we check every byte's address
      result = machine->WriteMem( vaddr, 1, (int)(buf[n++]) );

      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    return n;
}

void Create_Syscall(unsigned int vaddr, int len) {
    // Create the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  No
    // way to return errors, though...
    char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Create\n");
	delete buf;
	return;
    }

    buf[len]='\0';

    fileSystem->Create(buf,0);
    delete[] buf;
    return;
}

int Open_Syscall(unsigned int vaddr, int len) {
    // Open the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  If
    // the file is opened successfully, it is put in the address
    // space's file table and an id returned that can find the file
    // later.  If there are any errors, -1 is returned.
    char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *f;			// The new open file
    int id;				// The openfile id

    if (!buf) {
	printf("%s","Can't allocate kernel buffer in Open\n");
	return -1;
    }

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Open\n");
	delete[] buf;
	return -1;
    }

    buf[len]='\0';

    f = fileSystem->Open(buf);
    delete[] buf;

    if ( f ) {
	if ((id = currentThread->space->fileTable.Put(f)) == -1 )
	    delete f;
	return id;
    }
    else
	return -1;
}

void Write_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one. For disk files, the file is looked
    // up in the current address space's open file table and used as
    // the target of the write.
    
    char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output

    if ( id == ConsoleInput) return;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer for write!\n");
	return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
	    printf("%s","Bad pointer passed to to write: data not written\n");
	    delete[] buf;
	    return;
	}
    }

    if ( id == ConsoleOutput) {
      for (int ii=0; ii<len; ii++) {
	printf("%c",buf[ii]);
      }

    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    f->Write(buf, len);
	} else {
	    printf("%s","Bad OpenFileId passed to Write\n");
	    len = -1;
	}
    }

    delete[] buf;
}

int Read_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one.    We reuse len as the number of bytes
    // read, which is an unnessecary savings of space.
    char *buf;		// Kernel buffer for input
    OpenFile *f;	// Open file for output

    if ( id == ConsoleOutput) return -1;
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer in Read\n");
	return -1;
    }

    if ( id == ConsoleInput) {
      //Reading from the keyboard
      scanf("%s", buf);

      if ( copyout(vaddr, len, buf) == -1 ) {
	printf("%s","Bad pointer passed to Read: data not copied\n");
      }
    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    len = f->Read(buf, len);
	    if ( len > 0 ) {
	        //Read something from the file. Put into user's address space
  	        if ( copyout(vaddr, len, buf) == -1 ) {
		    printf("%s","Bad pointer passed to Read: data not copied\n");
		}
	    }
	} else {
	    printf("%s","Bad OpenFileId passed to Read\n");
	    len = -1;
	}
    }

    delete[] buf;
    return len;
}

void Close_Syscall(int fd) {
    // Close the file associated with id fd.  No error reporting.
    OpenFile *f = (OpenFile *) currentThread->space->fileTable.Remove(fd);

    if ( f ) {
      delete f;
    } else {
      printf("%s","Tried to close an unopen file\n");
    }
}

//--------Project2 Part 1 The implementation of each Syscall-----------------
//Lock
int CreateLock(unsigned int vaddr, int len, AddrSpace *space) { 
//Deal with char array argument
	char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return -1;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","CreateLock Error: Bad pointer passed to Create\n");
	delete buf;
	return -1;
    }
	
    buf[len]='\0';

//Begin to creatlock
	int index = -1;
	LockTableLock->Acquire();
	
	//Look for next availble lock
	for (int i=0; i<MAXLOCKNUM; i++ ) {
		if(	LockTable[i].lock == NULL) {
			index = i;
			break;
		}	
	}
	
	if(index == -1) {
		printf("CreateLock Error: No more Lock is avaible, we will shut you down!\n");
		delete[] buf;
		return -1;
	}
	
	LockTable[index].lock = new Lock(buf);
	LockTable[index].procSpace = space;
	LockTable[index].inUse = false;
	//printf("myDEBUG: CreateLock successful\n");
	
	LockTableLock->Release();
	delete[] buf;
    return index;
}

void DestroyLock(int index, AddrSpace *space){
	
	if (index < 0 || index >=MAXLOCKNUM) {
		printf("DestroyLock Error: invalid index!\n");
		return;
	}
	
	LockTableLock->Acquire();
	if (space!=LockTable[index].procSpace) {	//Different Process owener
		printf("Fatal DestroyLock Error: You are trying to destroy other process's Lock!\n");
		LockTableLock->Release();
		return;
	}
	
	if (LockTable[index].lock == NULL) {
		printf("DestroyLock Error: The lock has been destroyed!\n");
		LockTableLock->Release();
		return;
	}
	
	if (LockTable[index].inUse == true) {
		printf("Fatal DestroyLock Error: The lock you are trying to destroy is in USE!\n");
		LockTableLock->Release();
		return;
	}
	
		//Ready to destroy
	delete LockTable[index].lock;
	LockTable[index].lock = NULL;
	LockTable[index].procSpace = NULL;
	LockTable[index].inUse = false;
	
	
	LockTableLock->Release();
		//printf("myDEBUG: DestroyLock successful\n");
	return;
}

void AcquireLock(int index, AddrSpace *space){
	
	if (index < 0 || index >=MAXLOCKNUM) {
		printf("AcquireLock Error: invalid index is %d!\n",index);
		return;
	}
	//printf("myDEBUG:currentThread id %d In AcquireLock syscall: before lockTableLock->Acquire\n",currentThread->threadId);
	//LockTableLock->Acquire();
	//printf("myDEBUG:currentThread id %d In AcquireLock syscall: after lockTableLock->Acquire\n",currentThread->threadId);
	if (space!=LockTable[index].procSpace) {	//Different Process owener
		printf("Fatal AcquireLock Error: You are trying to Aquire other process's Lock!\n");
		//LockTableLock->Release();
		return;
	}
	
	if (LockTable[index].lock == NULL) {
		printf("AcquireLock Error: You are trying to acquire a lock that has not been created!\n");
		//LockTableLock->Release();
		return;
	}
	//LockTableLock->Release();
	//Valid to acquire
	//printf("myDEBUG:currentThread id %d In AcquireLock syscall is to acquire lock%d after lockTableLock->Acquire\n",currentThread->threadId,index);
	
	LockTable[index].lock->Acquire();
	//LockTableLockAcquire();
	LockTable[index].inUse = true;
	
	//printf("myDEBUG: currentThread %d AcquireLock successful\n",currentThread->threadId);
	//LockTableLockRelease();
	//printf("myDEBUG: currentThread %d in AcquireLock sys call released LockTableLock successful\n",currentThread->threadId);
	return;
}

void ReleaseLock(int index, AddrSpace *space){
	
	if (index < 0 || index >=MAXLOCKNUM) {
		printf("ReleaseLock Error: invalid index is %d!\n",index);
		return;
	}
	//printf("myDEBUG:currentThread id %d In ReleaseLock syscall: before lockTableLock->Acquire\n",currentThread->threadId);
	//LockTableLock->Acquire();
	//printf("myDEBUG:currentThread id %d In ReleaseLock syscall: after lockTableLock->Acquire\n",currentThread->threadId);
	if (space!=LockTable[index].procSpace) {	//Different Process owener
		printf("Fatal ReleaseLock Error: You are trying to Release other process's Lock!\n");
		//LockTableLock->Release();
		return;
	}
	

	if (LockTable[index].lock == NULL) {
		printf("ReleaseLock Error: You are trying to release a lock that has not been created!\n");
		//LockTableLock->Release();
		return;
	}
		//Valid to acquire
	LockTable[index].lock->Release();
	LockTable[index].inUse = false;
	
	//printf("myDEBUG: currentThread %d ReleaseLock successful\n", currentThread->threadId);
	//LockTableLock->Release();
	//printf("myDEBUG: currentThread %d in ReleaseLock sys call released LockTableLock successful\n",currentThread->threadId);
	return;
}

//CV

int CreateCondition(unsigned int vaddr, int len, AddrSpace *space) {
	//Deal with char array argument
	char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return -1;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","CreateCondition Error: Bad pointer passed to Create\n");
	delete buf;
	return -1;
    }
	
    buf[len]='\0';
	
//Begin to create CV
	int index = -1;
	CVTableLock->Acquire();
	
	//Look for next available CV
	for (int i=0; i<MAXCVNUM; i++) {
		if(CVTable[i].condition == NULL) {
			index = i;
			break;
		}
	}
	
	if(index == -1) {
		printf("CreateCondition Error: No more Condition is avaible, we will shut you down!\n");
		delete[] buf;
		return -1;
	}
	
	CVTable[index].condition = new Condition(buf);
	CVTable[index].procSpace = space;
	CVTable[index].numOfWait = 0;
	
	//printf("myDEBUG: CreateCondition successful\n");
	CVTableLock->Release();
	delete[] buf;
	return index;
}
	
void DestroyCondition(int index, AddrSpace *space){
	
	if (index < 0 || index >=MAXCVNUM) {
		printf("DestroyCondition Error: invalid index!\n");
		return;
	}
	
	CVTableLock->Acquire();
	
	if (space!=CVTable[index].procSpace) {	//Different Process owener
		printf("Fatal DestroyCV Error: You are trying to destroy other process's CV!\n");
		CVTableLock->Release();
		return;
	}
	
	if (CVTable[index].condition == NULL) {
		printf("DestroyCondition Error: The condition has been destroyed!\n");
		CVTableLock->Release();
		return;
	}
	
	if (CVTable[index].numOfWait > 0) {
		printf("Fatal DestroyCondition Error: The condition you are trying to destroy is in USE!\n");
		CVTableLock->Release();
		return;
	}
	
		//Ready to destroy
	delete CVTable[index].condition;
	CVTable[index].condition = NULL;
	CVTable[index].procSpace = NULL;
	CVTable[index].numOfWait = 0;
	
	CVTableLock->Release();
		//printf("myDEBUG: DestroyCondition successful\n");
	return;
}
	
void WaitCV(int lockIndex, int cvIndex, AddrSpace *space){
	
	if( lockIndex<0 || lockIndex>=MAXLOCKNUM) {
		printf("WaitCV Error: invalid lock index!\n");
		return;
	}
	
	if(cvIndex<0 || cvIndex>=MAXCVNUM) {
		printf("WaitCV Error: invalid CV index!\n");
		return;
	}
	//printf("myDEBUG: currentThread %d In WaitCV syscall before CVTableLock->Acquire and LockTableLock->Acquire\n", currentThread->threadId);
	//CVTableLock->Acquire();
	//LockTableLock->Acquire();
	//printf("myDEBUG: currentThread %d In WaitCV syscall after CVTableLock->Acquire and LockTableLock->Acquire\n", currentThread->threadId);
	
	if(space!=LockTable[lockIndex].procSpace) {
		printf("Fatal WaitCV Error: You are trying to Wait on other process's Lock!\n");
		//LockTableLock->Release();
		//CVTableLock->Release();
		return;	
	}
	
	if(space!=CVTable[cvIndex].procSpace) {
		printf("Fatal WaitCV Error: You are trying to Wait on other process's CV!\n");
		//LockTableLock->Release();
		//CVTableLock->Release();
		return;	
	}
	
	if(LockTable[lockIndex].lock ==NULL) {
		printf("WaitCV Error: You are trying to wait on a lock that has not been created!\n");
		//LockTableLock->Release();
		//CVTableLock->Release();
		return;	
	}
	
	if(CVTable[cvIndex].condition ==NULL) {
		printf("WaitCV Error: You are trying to wait on a CV that has not been created!\n");
		//LockTableLock->Release();
		//CVTableLock->Release();
		return;	
	}
	//Valid to wait
	CVTable[cvIndex].numOfWait++;
	//LockTableLock->Release();
	//CVTableLock ->Release();
	//printf("myDEBUG: currentThread %d In WaitCV syscall is going to wait for lock with index %d after CVTableLock->Release and LockTableLock->Release\n",lockIndex, currentThread->threadId);
	CVTable[cvIndex].condition->Wait(LockTable[lockIndex].lock);
	//LockTableLock->Acquire();
	//CVTableLock->Acquire();
	CVTable[cvIndex].numOfWait--;
	//LockTableLock->Release();
	//CVTableLock->Release();	
	//printf("myDEBUG: WaitCV successful\n");

	return;
	
}
	
void SignalCV(int lockIndex, int cvIndex, AddrSpace *space){
	
	if( lockIndex<0 || lockIndex>=MAXLOCKNUM) {
		printf("SignalCV Error: invalid lock index!\n");
		return;
	}
	
	if(cvIndex<0 || cvIndex>=MAXCVNUM) {
		printf("SignalCV Error: invalid CV index!\n");
		return;
	}
	//CVTableLock->Acquire();
	//LockTableLock->Acquire();
	
	if(space!=LockTable[lockIndex].procSpace) {
		printf("Fatal SignalCV Error: You are trying to Signal on other process's Lock!\n");
		//LockTableLock->Release();
		//CVTableLock->Release();
		return;	
	}
	
	if(space!=CVTable[cvIndex].procSpace) {
		printf("Fatal SignalCV Error: You are trying to Signal on other process's CV!\n");
		//LockTableLock->Release();
		//CVTableLock->Release();
		return;	
	}
	
	if(LockTable[lockIndex].lock ==NULL) {
		printf("SignalCV Error: You are trying to Signal on a lock that has not been created!\n");
		//LockTableLock->Release();
		//CVTableLock->Release();
		return;	
	}
	
	if(CVTable[cvIndex].condition ==NULL) {
		printf("SignalCV Error: You are trying to Signal on a CV that has not been created!\n");
		//LockTableLock->Release();
		//CVTableLock->Release();
		return;	
	}
	//Valid to Signal
	//printf("myDEBUG: currentThread %d In WaitCV syscall is going to Signal for lock with index %d\n",lockIndex, currentThread->threadId);
	CVTable[cvIndex].condition->Signal(LockTable[lockIndex].lock);

	
	//printf("myDEBUG: Signal successful\n");
	//LockTableLock->Release();
	//CVTableLock->Release();
	return;
	
}	
	
void BroadcastCV(int lockIndex, int cvIndex, AddrSpace *space){
	
	if( lockIndex<0 || lockIndex>=MAXLOCKNUM) {
		printf("BroadcastCV Error: invalid lock index!\n");
		return;
	}
	
	if(cvIndex<0 || cvIndex>=MAXCVNUM) {
		printf("BroadcastCV Error: invalid CV index!\n");
		return;
	}
	//CVTableLock->Acquire();
	//LockTableLock->Acquire();
	
	if(space!=LockTable[lockIndex].procSpace) {
		printf("Fatal BroadcastCV  Error: You are trying to Broadcast on other process's Lock!\n");
		//LockTableLock->Release();
		//CVTableLock->Release();
		return;	
	}
	
	if(space!=CVTable[cvIndex].procSpace) {
		printf("Fatal BroadcastCV  Error: You are trying to Broadcast on other process's CV!\n");
		//LockTableLock->Release();
		//CVTableLock->Release();
		return;	
	}
	
	if(LockTable[lockIndex].lock ==NULL) {
		printf("BroadcastCV Error: You are trying to Broadcast on a lock that has not been created!\n");
		//LockTableLock->Release();
		//CVTableLock->Release();
		return;	
	}
	
	if(CVTable[cvIndex].condition ==NULL) {
		printf("BroadcastCV  Error: You are trying to Broadcast on a CV that has not been created!\n");
		//LockTableLock->Release();
		//CVTableLock->Release();
		return;	
	}
	//Valid to Signal	
	CVTable[cvIndex].condition->Broadcast(LockTable[lockIndex].lock);
	//LockTableLock->Release();
	//CVTableLock->Release();
	
	//printf("myDEBUG: Broadcast successful\n");

	return;
	
}

void kernel_thread(int vaddr){
	machine->WriteRegister(PCReg, vaddr);
	machine->WriteRegister(NextPCReg,vaddr+4);
	machine->WriteRegister(StackReg, (currentThread->space->getnumPages() + divRoundUp(UserStackSize,PageSize)*(currentThread->threadId))*PageSize - 16);
	currentThread->space->RestoreState();
	machine->Run();
}
//Some funtional system call used in part3
int Rand(int upperBound) { 
	if(upperBound<=0) {
		printf("Error: the upperBound must be positive");
		return -1;
	}
	int result = -1;
	srand ( time(NULL) );
	result = rand() % upperBound;
    return result;
}
int InputInt( ){
	int result = 0;
	scanf("%d",&result);
	fflush(stdin);
	return result;
}

void Fork_Syscall(int vaddr){
	Thread *t;
	ProcessTableLock->Acquire();
	t = procTable->addThread(currentThread->spaceId);
	if(t == NULL){
		ProcessTableLock->Release();
		printf("ERROR: addThread return NULL when call Fork_Syscall!\n");
		return;
	}
	ProcessTableLock->Release();
	t->space = currentThread->space;
	t->spaceId = currentThread->spaceId;
	#ifdef NETWORK	
	processMailboxLock->Acquire();
	t->mailbox = processMailbox;
	//processMailbox is global varible
	if(processMailbox>=50)
	processMailbox=0;
	else
	{
	processMailbox++;
	}
	processMailboxLock->Release();
	printf("\nMy mailbox is %d\n",t->mailbox);
#endif	
	t->Fork((VoidFunctionPtr)kernel_thread, vaddr);
}

void Exit_Syscall(int status){
	bool isLastP;
	int isLastT;
	ProcessTableLock->Acquire();
	isLastP = procTable->isLastProcess(currentThread->spaceId);
	isLastT = procTable->isLastThread(currentThread->spaceId);
	if(isLastT == -1){
		ProcessTableLock->Release();
		DEBUG('a',"ERROR: invalid spaceId when call Exit_Syscall!\n");
		return;
	}

	if(status != 0){	//abnormal exit, shut down the process
		printf("ERROR: Exit process initiated by user!\n");
		procTable->deleteProcess(currentThread->spaceId);
		ProcessTableLock->Release();
		currentThread->Finish();
	}else{	//normal exit
		if((isLastP)&&(isLastT == 1)){	//last process last thread, halt
			ProcessTableLock->Release();
			interrupt->Halt();	//last process last thread 
		}else if(isLastT != 1){	//not last thread, delete thread
			DEBUG('a',"to exit thread with thread id %d\n", currentThread->threadId);
			procTable->deleteThread(currentThread->spaceId, currentThread);
			DEBUG('a',"Exit thread with thread id %d and space id %d", currentThread->threadId,currentThread->spaceId);
			ProcessTableLock->Release();
			currentThread->Finish();
			} else{	//last thread not last process, delete process
					procTable->deleteProcess(currentThread->spaceId);
					ProcessTableLock->Release();
					currentThread->Finish();
				}
	}
}

void exec_kernel(int i){
	currentThread->space->InitRegisters();
	currentThread->space->RestoreState();
	machine->Run();

}

SpaceId Exec_Syscall(int vaddr, int len){
	char *buf = new char[len+1];
	Thread *t;
	AddrSpace *space;
	SpaceId id;
	if(!buf){
		printf("ERROR: buf error when Exec_Syscall\n");
		return -1;
	}
	
	if(copyin(vaddr, len, buf) == -1){
		delete buf;
		printf("ERROR: Bad virtualAddress in Exec_Syscall\n");
		return -1;
	}
	buf[len]='\0';
	
	OpenFile *executable = fileSystem->Open(buf);
	DEBUG('a',"name of executable file is %s \n",buf);
	if(executable == NULL){
		printf("ERROR: can't open executable file\n");
		return -1;
	}
	ProcessTableLock->Acquire();
	if(procTable->isProcessFull(currentThread->spaceId)){
		ProcessTableLock->Release();
		printf("ERROR: process number meets limit in Exec_Syscall.\n");
		return -1;
	}
	
	id = procTable->FindSpaceId();

	if(id == -1){
		ProcessTableLock->Release();
		printf("ERROR: SpaceId pool full when call Exec_Syscall\n");
		return -1;
	}
	space = new AddrSpace(executable, id);
	if(space == NULL){
		ProcessTableLock->Release();
		printf("DEBUG: create new AddrSpace fail when call Exec_Syscall!\n");
		return -1;
	}
   	t = new Thread("exec_kernal");
	if(t == NULL){
		ProcessTableLock->Release();
		printf("DEBUG: create new Thread fail when call Exec_Syscall!\n");
		return -1;
	}
	t->space = space;
	t->spaceId = id;
#ifdef NETWORK	
	processMailboxLock->Acquire();
	t->mailbox = processMailbox;
	//processMailbox is global varible
	if(processMailbox>=50)
	processMailbox=0;
	else
	{
	processMailbox++;
	}
	processMailboxLock->Release();
	printf("\nMy mailbox is %d\n",t->mailbox);
#endif	
	if((procTable->addProcess(buf, len, space, id, t)) == -1){
		ProcessTableLock->Release();
		printf("ERROR: Fail to addProcess when call Exec_Syscall.\n");
		return -1;
	}
	//delete executable;
	delete [] buf;
	t->Fork((VoidFunctionPtr)exec_kernel,0);
	ProcessTableLock->Release();
	return id;
}



//Project3 Part3: Networking
#ifdef NETWORK
int
randServer(){
return rand()%serverSize;
}
int rpcCreateLock(unsigned int vaddr, int len,int procMailbox){
	
	//Deal with char array argument
	char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return -1;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","rpcCreateLock Error: Bad pointer passed to Create\n");
	delete buf;
	return -1;
    }
	
    buf[len]='\0';
	
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	RpcReqMessage requestMessage;

	RpcRplyMessage replyMessage;
	
	char* sendData;
	char recvBuff[MaxMailSize];
	
	//The request message format
	requestMessage.reqType = CREATELOCK;
	requestMessage.intArgNum = 0;
	strcpy(requestMessage.strArg, buf);
	sendData = (char*) (&requestMessage);
	
	//The sock
	
	outPktHdr.to = randServer();
	
	//printf("\ntangDebug:: In excpetion.cc send request to server %d\n", outPktHdr.to);//tangTest
	outMailHdr.to = SEVERMAILBOX;
	outMailHdr.from = procMailbox;
	outMailHdr.length = sizeof(requestMessage);

	bool success = postOffice->Send(outPktHdr, outMailHdr, sendData); 
    if ( !success ) {
      printf("Fatal Error: rpcCreateLock : The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }	
	delete buf; 
	
	postOffice->Receive(procMailbox, &inPktHdr, &inMailHdr, recvBuff);
	fflush(stdout);
	//printf("\ntangDebug:: get Reply from server %d\n", inPktHdr.from);
	replyMessage = *((struct RpcRplyMessage*) recvBuff);
	//printf("\ntangDebug:: reply type is %d", replyMessage.rplyType);
	if(replyMessage.rplyType != CREATELOCK) {
		printf("Fatal Error: rpcCreateLock: The reply message is not for createLock!\n");
		interrupt->Halt();
	}
	
	if(replyMessage.status == NOTOK) {
		printf("Error: rpcCreateLock\n");
		return replyMessage.retValue;
	}

	printf("Sucessfully rpcCreateLock %d\n",replyMessage.retValue);
	return replyMessage.retValue;
	

}

void rpcDestroyLock(int index, int procMailbox) {
	if (index < 0 || index >=MAXLOCKNUM) {
		printf("rpcDestroyLock Error: invalid index!\n");
		return;
	}
	
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	RpcReqMessage requestMessage;

	RpcRplyMessage replyMessage;
	
	char* sendData;
	char recvBuff[MaxMailSize];
	
	//The request message format
	requestMessage.reqType = DESTROYLOCK;
	requestMessage.intArgNum = 1;
	requestMessage.intArg[0]=index;
	sendData = (char*) (&requestMessage);
	
	//The sock
	outPktHdr.to = randServer();
	outMailHdr.to = SEVERMAILBOX;
	outMailHdr.from = procMailbox;
	outMailHdr.length = sizeof(requestMessage);

	bool success = postOffice->Send(outPktHdr, outMailHdr, sendData); 
    if ( !success ) {
      printf("Fatal Error: rpcDestoryLock : The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }	

	postOffice->Receive(procMailbox, &inPktHdr, &inMailHdr, recvBuff);
	fflush(stdout);
	
	replyMessage = *((struct RpcRplyMessage*) recvBuff);
	if(replyMessage.rplyType != DESTROYLOCK) {
		printf("Fatal Error: rpcDestoryLock: The reply message is not for destoryLock!\n");
		interrupt->Halt();
	}
	
	if(replyMessage.status == NOTOK) {
		printf("Error: rpcDestoryLock\n");
	}
	
	if(replyMessage.status == OK) {
		printf("rpcDestoryLock sucessful!\n");
	}
	

}
void rpcAcquireLock(int index, int procMailbox) {
	if (index < 0 || index >=MAXLOCKNUM) {
		printf("rpcAcquireLock Error: invalid index is %d!\n",index);
		return;
	}
	
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	RpcReqMessage requestMessage;
	RpcRplyMessage replyMessage;
	
	char* sendData;
	char recvBuff[MaxMailSize];
	
	//The request message format
	requestMessage.reqType = ACQUIRELOCK;
	requestMessage.intArgNum = 1;
	requestMessage.intArg[0]=index;

	sendData = (char*) (&requestMessage);
	
	//The sock
	outPktHdr.to = randServer();
	outMailHdr.to = SEVERMAILBOX;
	outMailHdr.from = procMailbox;
	outMailHdr.length = sizeof(requestMessage);
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, sendData); 
    if ( !success ) {
      printf("Fatal Error: rpcAcquireLock : The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }

	postOffice->Receive(procMailbox, &inPktHdr, &inMailHdr, recvBuff);
	fflush(stdout);
	replyMessage = *((struct RpcRplyMessage*) recvBuff);
	
	if(replyMessage.rplyType != ACQUIRELOCK) {
		printf("Fatal Error: rpcAquireLock: The reply message is not for destoryLock!\n");
		interrupt->Halt();
	}
	
	if(replyMessage.status == NOTOK) {
		printf("Error: rpcAcquireLock\n");
	}
	
	else {
		printf("rpcAcquireLock sucessful!\n");
	}
}

void rpcReleaseLock(int index, int procMailbox){
	if (index < 0 || index >=MAXLOCKNUM) {
		printf("rpcReleaseLock Error: invalid index is %d!\n",index);
		return;
	}
	
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	RpcReqMessage requestMessage;
	RpcRplyMessage replyMessage;
	
	char* sendData;
	char recvBuff[MaxMailSize];
	
	//The request message format
	requestMessage.reqType = RELEASELOCK;
	requestMessage.intArgNum = 1;
	requestMessage.intArg[0]=index;

	sendData = (char*) (&requestMessage);
	
	//The sock
	outPktHdr.to = randServer();
	outMailHdr.to = SEVERMAILBOX;
	outMailHdr.from = procMailbox;
	outMailHdr.length = sizeof(requestMessage);
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, sendData); 
    if ( !success ) {
      printf("Fatal Error: rpcAcquireLock : The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	
	postOffice->Receive(procMailbox, &inPktHdr, &inMailHdr, recvBuff);
	fflush(stdout);
	replyMessage = *((struct RpcRplyMessage*) recvBuff);
	
	if(replyMessage.rplyType != RELEASELOCK) {
		printf("Fatal Error: rpcReleaseLock: The reply message is not for ReleaseLock!\n");
		interrupt->Halt();
	}
	
	if(replyMessage.status == NOTOK) {
		printf("Error: rpcReleaseLock\n");
	}
	
	else {
		printf("rpcReleaseLock sucessful!\n");
	}

}


int rpcCreateCondition(unsigned int vaddr, int len, int procMailbox) {
	char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return -1;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","rpcCreateLock Error: Bad pointer passed to Create\n");
	delete buf;
	return -1;
    }
	
    buf[len]='\0';
	
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	RpcReqMessage requestMessage;
	RpcRplyMessage replyMessage;
	
	char* sendData;
	char recvBuff[MaxMailSize];
	
	//The request message format
	requestMessage.reqType = CREATECV;
	requestMessage.intArgNum = 0;
	strcpy(requestMessage.strArg, buf);
	sendData = (char*) (&requestMessage);
	//The sock
	outPktHdr.to = randServer();
	outMailHdr.to = SEVERMAILBOX;
	outMailHdr.from = procMailbox;
	outMailHdr.length = sizeof(requestMessage);
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, sendData); 
    if ( !success ) {
      printf("Fatal Error: rpcCreateCV : The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }	
	delete buf; 
	
	postOffice->Receive(procMailbox, &inPktHdr, &inMailHdr, recvBuff);
	fflush(stdout);
	
	replyMessage = *((struct RpcRplyMessage*) recvBuff);
	
	if(replyMessage.rplyType != CREATECV) {
		printf("Fatal Error: rpcCreateCondition: The reply message is not for createCondition!\n");
		interrupt->Halt();
	}
	
	if(replyMessage.status == NOTOK) {
		printf("Error: rpcCreateContition\n");
		return replyMessage.retValue;
	}
	
	printf("Successfully create a CV of index# %d\n",replyMessage.retValue);
	return replyMessage.retValue;
}

void rpcDestroyCondition(int index, int procMailbox){
	if (index < 0 || index >=MAXLOCKNUM) {
		printf("rpcDestroyLock Error: invalid index!\n");
		return;
	}
	
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	RpcReqMessage requestMessage;
	RpcRplyMessage replyMessage;
	
	char* sendData;
	char recvBuff[MaxMailSize];
	
	//The request message format
	requestMessage.reqType = DESTROYCV;
	requestMessage.intArgNum = 1;
	requestMessage.intArg[0]=index;
	sendData = (char*) (&requestMessage);
	
	//The sock
	outPktHdr.to = randServer();
	outMailHdr.to = SEVERMAILBOX;
	outMailHdr.from = procMailbox;
	outMailHdr.length = sizeof(requestMessage);

	bool success = postOffice->Send(outPktHdr, outMailHdr, sendData); 
    if ( !success ) {
      printf("Fatal Error: rpcDestroyCondition : The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }	

	postOffice->Receive(procMailbox, &inPktHdr, &inMailHdr, recvBuff);
	fflush(stdout);
	replyMessage = *((struct RpcRplyMessage*) recvBuff);

	if(replyMessage.rplyType != DESTROYCV) {
		printf("Fatal Error: rpcDestoryCV: The reply message is not for destoryCV!\n");
		interrupt->Halt();
	}
	
	if(replyMessage.status == NOTOK) {
		printf("Error: rpcDestoryCV\n");
	}
	
	if(replyMessage.status == OK) {
		printf("rpcDestoryCV sucessful!\n");
	}

}

void rpcWaitCV (int lockIndex, int cvIndex, int procMailbox){

	if( lockIndex<0 || lockIndex>=MAXLOCKNUM) {
		printf("rpcWaitCV Error: invalid lock index!\n");
		return;
	}
	
	if(cvIndex<0 || cvIndex>=MAXCVNUM) {
		printf("rpcWaitCV Error: invalid CV index!\n");
		return;
	}
	
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	RpcReqMessage requestMessage;
	RpcRplyMessage replyMessage;
	
	char* sendData;
	char recvBuff[MaxMailSize];
	
	//The request message format
	requestMessage.reqType = WAITCV;
	requestMessage.intArgNum = 2;
	requestMessage.intArg[0]=lockIndex;
	requestMessage.intArg[1]=cvIndex;
	
	sendData = (char*) (&requestMessage);
	
	//The sock
	outPktHdr.to = randServer();
	outMailHdr.to = SEVERMAILBOX;
	outMailHdr.from = procMailbox;
	outMailHdr.length = sizeof(requestMessage);
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, sendData); 
    if ( !success ) {
      printf("Fatal Error: rpcWaitCV : The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }

	postOffice->Receive(procMailbox, &inPktHdr, &inMailHdr, recvBuff);
	fflush(stdout);
	replyMessage = *((struct RpcRplyMessage*) recvBuff);
	
	
	if(replyMessage.rplyType != WAITCV) {
		printf("Fatal Error: rpcWaitCV: The reply message is not for rpcWaitCV!\n");
		interrupt->Halt();
	}
	
	if(replyMessage.status == NOTOK) {
		printf("Error: rpcWaitCV\n");
	}
	
	else {
		//I waked up, so I have to acquire the lock
		rpcAcquireLock(lockIndex,procMailbox);
	
		printf("rpcWaitCV sucessful!\n");
	}
}

void rpcSignalCV (int lockIndex, int cvIndex, int procMailbox) {
	
	if( lockIndex<0 || lockIndex>=MAXLOCKNUM) {
		printf("rpcSignalCV Error: invalid lock index!\n");
		return;
	}
	
	if(cvIndex<0 || cvIndex>=MAXCVNUM) {
		printf("rpcSignalCV Error: invalid CV index!\n");
		return;
	}
	
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	RpcReqMessage requestMessage;
	RpcRplyMessage replyMessage;
	
	char* sendData;
	char recvBuff[MaxMailSize];
	
	//The request message format
	requestMessage.reqType = SIGNALCV;
	requestMessage.intArgNum = 2;
	requestMessage.intArg[0]=lockIndex;
	requestMessage.intArg[1]=cvIndex;
	
	sendData = (char*) (&requestMessage);
	
	//The sock
	outPktHdr.to = randServer();
	outMailHdr.to = SEVERMAILBOX;
	outMailHdr.from = procMailbox;
	outMailHdr.length = sizeof(requestMessage);
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, sendData); 
    if ( !success ) {
      printf("Fatal Error: rpcSignalCV : The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	
	postOffice->Receive(procMailbox, &inPktHdr, &inMailHdr, recvBuff);
	fflush(stdout);
	replyMessage = *((struct RpcRplyMessage*) recvBuff);
	
	if(replyMessage.rplyType != SIGNALCV) {
		printf("Fatal Error: rpcSignalCV: The reply message is not for rpcSignalCV!\n");
		printf("rpcDebug: the wrong type is %d\n",replyMessage.rplyType);
		interrupt->Halt();
	}
	
	
	if(replyMessage.status == OK) {
		printf("rpcSignalCV sucessful!\n");		
	}
	
	else {
		printf("Error: rpcSignalCV\n");
	}
	
	

}

void rpcBroadcastCV (int lockIndex, int cvIndex, int procMailbox){
	if( lockIndex<0 || lockIndex>=MAXLOCKNUM) {
		printf("rpcBroadcastCV Error: invalid lock index!\n");
		return;
	}
	
	if(cvIndex<0 || cvIndex>=MAXCVNUM) {
		printf("rpcBroadcastCV Error: invalid CV index!\n");
		return;
	}
	
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	RpcReqMessage requestMessage;
	RpcRplyMessage replyMessage;
	
	char* sendData;
	char recvBuff[MaxMailSize];
	
	//The request message format
	requestMessage.reqType = BROADCASTCV;
	requestMessage.intArgNum = 2;
	requestMessage.intArg[0]=lockIndex;
	requestMessage.intArg[1]=cvIndex;
	
	sendData = (char*) (&requestMessage);
	
	//The sock
	outPktHdr.to = randServer();
	outMailHdr.to = SEVERMAILBOX;
	outMailHdr.from = procMailbox;
	outMailHdr.length = sizeof(requestMessage);
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, sendData); 
    if ( !success ) {
      printf("Fatal Error: rpcBroadcastCV : The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	
	postOffice->Receive(procMailbox, &inPktHdr, &inMailHdr, recvBuff);
	fflush(stdout);
	replyMessage = *((struct RpcRplyMessage*) recvBuff);
	
	if(replyMessage.rplyType != BROADCASTCV) {
		printf("Fatal Error: rpcBroadcastCV: The reply message is not for rpcBroadcastCV!\n");
		interrupt->Halt();
	}
	
	
	if(replyMessage.status == OK) {
		printf("rpcBroadcastCV sucessful!\n");		
	}
	
	else {
		printf("Error: rpcBroadcastCV\n");
	}
}

int rpcCreateMV(unsigned int vaddr, int len, int arrayNum, int iniValue, int procMailbox) {
	char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return -1;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","CreateLock Error: Bad pointer passed to Create\n");
	delete buf;
	return -1;
    }
	
    buf[len]='\0';

	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	RpcReqMessage requestMessage;

	RpcRplyMessage replyMessage;
	
	char* sendData;
	char recvBuff[MaxMailSize];
	
	//The request message format
	requestMessage.reqType = CREATEMV;
	requestMessage.intArgNum = 2;
	requestMessage.intArg[0]=arrayNum;
	requestMessage.intArg[1]=iniValue;
	
	strcpy(requestMessage.strArg, buf);
	
	sendData = (char*) (&requestMessage);
	
	//The sock
	outPktHdr.to = randServer();
	outMailHdr.to = SEVERMAILBOX;
	outMailHdr.from = procMailbox;
	outMailHdr.length = sizeof(requestMessage);	
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, sendData); 
    if ( !success ) {
      printf("Fatal Error: rpcCreateLock : The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }	
	delete buf;
	postOffice->Receive(procMailbox, &inPktHdr, &inMailHdr, recvBuff);
	fflush(stdout);
	
	replyMessage = *((struct RpcRplyMessage*) recvBuff);
	
	if(replyMessage.rplyType != CREATEMV) {
		printf("Fatal Error: rpcCreateMV: The reply message is not for createMV!\n");
		interrupt->Halt();
	}
	
	if(replyMessage.status == NOTOK) {
		printf("Error: rpcCreateMV\n");
		return replyMessage.retValue;
		
	}
	
	printf("Successfully create a MV of # %d\n",replyMessage.retValue);
	
	return replyMessage.retValue;
	
}

void rpcDestroyMV(int index, int procMailbox){
	if (index < 0 || index >=MAXMVNUM) {
		printf("rpcDestroymv Error: invalid index!\n");
		return;
	}

	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	RpcReqMessage requestMessage;
	RpcRplyMessage replyMessage;
	
	char* sendData;
	char recvBuff[MaxMailSize];
	
	//The request message format
	requestMessage.reqType = DESTROYMV;
	requestMessage.intArgNum = 1;
	requestMessage.intArg[0]=index;
	sendData = (char*) (&requestMessage);
	
	//The sock
	outPktHdr.to = randServer();
	outMailHdr.to = SEVERMAILBOX;
	outMailHdr.from = procMailbox;
	outMailHdr.length = sizeof(requestMessage);

	bool success = postOffice->Send(outPktHdr, outMailHdr, sendData); 
    if ( !success ) {
      printf("Fatal Error: rpcDestoryMV : The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }	

	postOffice->Receive(procMailbox, &inPktHdr, &inMailHdr, recvBuff);
	fflush(stdout);
	
	replyMessage = *((struct RpcRplyMessage*) recvBuff);
	
	if(replyMessage.rplyType != DESTROYMV) {
		printf("Fatal Error: rpcDestoryMV: The reply message is not for destoryMV!\n");
		interrupt->Halt();
	}
	
	if(replyMessage.status == NOTOK) {
		printf("Error: rpcDestoryMV\n");
	}
	
	if(replyMessage.status == OK) {
		printf("rpcDestoryMV sucessful!\n");
	}

}


int rpcGetValueMV(int mvIndex, int arrayIndex, int procMailbox){
	if (mvIndex < 0 || mvIndex >=MAXMVNUM) {
		printf("rpcGetValueMV Error: invalid index is %d!\n",mvIndex);
		return WRONGVALUE;
	}
	
	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	RpcReqMessage requestMessage;
	RpcRplyMessage replyMessage;
	
	char* sendData;
	char recvBuff[MaxMailSize];
	
	//The request message format
	requestMessage.reqType = GETVALUEMV;
	requestMessage.intArgNum = 2;
	requestMessage.intArg[0]=mvIndex;
	requestMessage.intArg[1]=arrayIndex;
	sendData = (char*) (&requestMessage);
	
	//The sock
	outPktHdr.to = randServer();
	outMailHdr.to = SEVERMAILBOX;
	outMailHdr.from = procMailbox;
	outMailHdr.length = sizeof(requestMessage);
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, sendData); 
    if ( !success ) {
      printf("Fatal Error: rpcDestoryMV : The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }	

	postOffice->Receive(procMailbox, &inPktHdr, &inMailHdr, recvBuff);
	fflush(stdout);
	
	replyMessage = *((struct RpcRplyMessage*) recvBuff);
	
	if(replyMessage.rplyType != GETVALUEMV) {
		printf("Fatal Error: rpcGetValueMV: The reply message is not for GetValueMV!\n");
		interrupt->Halt();
	}
	
	if(replyMessage.status == NOTOK) {
		printf("Error: rpcGetValueMV\n");
	}
	
	return replyMessage.retValue;

}

void rpcSetValueMV(int mvIndex, int arrayIndex, int value, int procMailbox){
	if (mvIndex < 0 || mvIndex >=MAXMVNUM) {
		printf("rpcSetValueMV Error: invalid index is %d!\n",mvIndex);
		return;
	}

	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;
	RpcReqMessage requestMessage;
	RpcRplyMessage replyMessage;
	
	char* sendData;
	char recvBuff[MaxMailSize];
	
	//The request message format
	requestMessage.reqType = SETVALUEMV;
	requestMessage.intArgNum = 3;
	requestMessage.intArg[0]=mvIndex;
	requestMessage.intArg[1]=arrayIndex;
	requestMessage.intArg[2]=value;
	sendData = (char*) (&requestMessage);
	
	//The sock
	outPktHdr.to = randServer();
	outMailHdr.to = SEVERMAILBOX;
	outMailHdr.from = procMailbox;
	outMailHdr.length = sizeof(requestMessage);
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, sendData); 
    if ( !success ) {
      printf("Fatal Error: rpcSetValueMV : The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }	
	
	postOffice->Receive(procMailbox, &inPktHdr, &inMailHdr, recvBuff);
	fflush(stdout);
	
	replyMessage = *((struct RpcRplyMessage*) recvBuff);
	
	if(replyMessage.rplyType != SETVALUEMV) {
		printf("Fatal Error: rpcSetValueMV: The reply message is not for SetValueMV!\n");
		interrupt->Halt();
	}
	
	if(replyMessage.status == NOTOK) {
		printf("Error: rpcSetValueMV\n");
	}
	
	if(replyMessage.status == OK) {
		printf("rpcSetValueMV sucessful!\n");
	}
	
}


#endif


int selectPage(int type){
	int page;
	int useFlag;
	void *pagePtr;
	if(type == RANDOM) {
		//iptLock->Acquire();
		do{
			page = rand() % NumPhysPages;

		} while(ipt[page].use == true);
		//ipt[page].use = true;
		//iptLock->Release();

	}
	if(type == FIFO) {

		pagePtr = ppnFIFOList->GetFirstElement();
		page = ((IPTEntry *) pagePtr)->physicalPage;
		
		//printf("tangDebug: Checking ipt[%d] use bit true or not\n", page);
		while(ipt[page].use == true){
			//printf("tangDebug: Checking ipt[%d] use bit true\n", page);
			pagePtr = ppnFIFOList->GetNextItem(pagePtr);
			if(pagePtr == NULL){
				printf("\nError: all page is in use!");
				return -1;
			}
			page = ((IPTEntry *) pagePtr)->physicalPage;
			
		}
		//printf("tangDebug: selected page in FIFO is ipt[%d] by process %d thread id %d\n", page, currentThread->spaceId, currentThread->threadId);
		if(ppnFIFOList->RemoveIndicatedOne(pagePtr) == -1){
			printf("tangDebug: Fail to remove the ipt[%d] from list by process %d thread id %d\n",page,currentThread->spaceId, currentThread->threadId);

		}
	}
	return page; 
}


//Step4: Swap1
int handleMemoryFull(int vpn){
	
	int physPage, swapPage, swapOffset;
	//Select a physcal page (Random & FIFO) and mark it as use
	

	
	iptLock->Acquire();


	physPage = selectPage(pageReplaceType);
	if(physPage == -1){
		printf("Error: can't evict a page and physical Memory is FULL!\n");
	}
	AddrSpace *dirtySpace = ipt[physPage].space;
	dirtySpace->pageTableLock->Acquire();
	int dirtyFlag = ipt[physPage].dirty;
	int validFlag = ipt[physPage].valid;

	int dirtyVpn = ipt[physPage].virtualPage;

	newPageTableEntry *updatedPageTable = dirtySpace->getPageTable();
	ipt[physPage].use = true;
	iptLock->Release();

	//selected page to evict is empty
	if(validFlag == false) return physPage;
	
	//Check if the physcal page is in the TLB or not
	IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts
	for (int i=0;i<TLBSize;i++) {
		if(machine->tlb[i].physicalPage == physPage && machine->tlb[i].valid == true) {
			ipt[physPage].dirty = machine->tlb[i].dirty;
			machine->tlb[i].valid = false;
		}
	}
	(void) interrupt->SetLevel(oldLevel);	//restore interrupts and return
	//Check dirty bit

	
	if(validFlag == true){
		if(dirtyFlag == true){
			//swap the selected physical memory to swap file
			
			if(updatedPageTable[dirtyVpn].diskLocation != SWAPFILE){	//choose a fixed location for the page to be first swap out
				swapLock->Acquire();
				swapPage = swapMap->Find();
				if(swapPage == -1){
					printf("\nERROR: SWAP FILE FULL!");
					return -1;
				}
				swapLock->Release();
				swapOffset = swapPage * PageSize;
				//dirtySpace->pageTableLock->Acquire();
				updatedPageTable[dirtyVpn].diskLocation = SWAPFILE;
				updatedPageTable[dirtyVpn].byteOffset = swapOffset;
				//dirtySpace->pageTableLock->Release();
				//printf("tangdebug: Allocating swap[%d] to vpn[%d] process %d thread %d\n", swapPage, dirtyVpn, currentThread->spaceId, currentThread->threadId);
			}










			//printf("myDEBUG: write in SWAP at position %d by process with id %d\n", swapOffset, ipt[physPage].processId);
			swapFile->WriteAt(&(machine->mainMemory[physPage*PageSize]), PageSize, updatedPageTable[dirtyVpn].byteOffset);
			//printf("tangdebug: Loading from ipt[%d] for vpn[%d] to swap page %d process %d thread %d \n",physPage,ipt[physPage].virtualPage, updatedPageTable[dirtyVpn].byteOffset/PageSize, currentThread->spaceId, currentThread->threadId);
			//updatePageTable




		}
		//dirtySpace->pageTableLock->Acquire();





		updatedPageTable[dirtyVpn].physicalPage = -1;
		updatedPageTable[dirtyVpn].valid = false;
		updatedPageTable[dirtyVpn].dirty = dirtyFlag;
		//dirtySpace->pageTableLock->Release();

	}

	dirtySpace->pageTableLock->Release();
	return physPage;

}

int IptMissHandler(int vpn) {
	//printf("DEBUG: I am into iptMissHander\n");
	AddrSpace *currentSpace = currentThread->space;
    newPageTableEntry *currentPageTable = currentSpace->getPageTable();
	
	int phyPageID = -1;
	int phyAddr = -1;
	int *physPage;

	
	//Allocate a page of memory (Find)
	PhysMapLock->Acquire();
	phyPageID = PhysMap->Find();
	//PhysMap->Print();
	PhysMapLock->Release();
	
	if(phyPageID == -1){
		//printf("\nStep3 problem! Physical address is full!\n\n\n\n\n\n");
		//Step 4
		phyPageID = handleMemoryFull(vpn);
		//printf("tangdebug: physical memory Full\n");
	}
	//else
		iptLock->Acquire();
		//ipt[phyPageID].use = true;
		//iptLock->Release();
	
	
	phyAddr = phyPageID*PageSize;
	
	//Update the pageTable

	//printf("DEBUG: VirtualPage[%d] was give physicalPage %d\n", vpn, phyPageID);
	currentSpace->pageTableLock->Acquire();
	

	if(vpn>=currentSpace->getmaxnumPages()){
		printf("DEBUG: In IptMissHandler VPN: %d, currentprocess maxnumPages %d\n", vpn, currentThread->space->getmaxnumPages());	
	}
	currentPageTable[vpn].physicalPage = phyPageID;
	currentPageTable[vpn].valid = true;
	//If the virtual page is from execuatable(code&uninialize date), ReadAt
	//if(currentPageTable[vpn].diskLocation == NOTONDISK)
		//printf("tangdebug: Loading to ipt[%d] for stack vpn[%d] by process %d thread %d \n",phyPageID, vpn, currentThread->spaceId, currentThread->threadId);
	//
	
	if(currentPageTable[vpn].diskLocation == EXECUTABLE) {
		currentSpace->currentExecutable->ReadAt(&(machine->mainMemory[phyAddr]), PageSize, currentPageTable[vpn].byteOffset);
		//printf("tangdebug: Loading to ipt[%d] for vpn[%d] from executable page %d process %d thread %d \n",phyPageID, vpn, currentPageTable[vpn].byteOffset/PageSize,currentThread->spaceId, currentThread->threadId);
	}
	if(currentPageTable[vpn].diskLocation == SWAPFILE) {
		//printf("myDEBUG: Read from swapfile at position %d by process with id %d\n",currentPageTable[vpn].byteOffset, currentPageTable[vpn].processId );
		swapFile->ReadAt(&(machine->mainMemory[phyAddr]), PageSize, currentPageTable[vpn].byteOffset);
		//printf("tangdebug: Loading to ipt[%d] for vpn[%d] from swap page %d process %d thread %d \n",phyPageID, vpn, currentPageTable[vpn].byteOffset/PageSize,currentThread->spaceId, currentThread->threadId);
	
		//swapLock->Acquire();
		//swapMap->Clear(currentPageTable[vpn].byteOffset/PageSize);
		//swapLock->Release();
		//currentPageTable[vpn].diskLocation = NOTONDISK;
		//currentPageTable[vpn].byteOffset = -1;
	}

	currentSpace->pageTableLock->Release();
	//Update the ipt
	//iptLock->Acquire();
	if(pageReplaceType == FIFO){
		IPTEntry* tmpIpt = &ipt[phyPageID];
		ppnFIFOList->Append((void *)tmpIpt);
		//printf("tangDebug: ipt[%d] is added to queue in FIFO by process %d thread %d\n", phyPageID, currentThread->spaceId, currentThread->threadId);
	}
	ipt[phyPageID].virtualPage = vpn;
	ipt[phyPageID].physicalPage = currentPageTable[vpn].physicalPage;
	ipt[phyPageID].valid = true;
	ipt[phyPageID].dirty = currentPageTable[vpn].dirty;
	ipt[phyPageID].processId = currentThread->spaceId;
	ipt[phyPageID].readOnly = currentPageTable[vpn].readOnly;
	ipt[phyPageID].space = currentThread->space; //ipt[phyPageID].space = currentPageTable[vpn].space
	iptLock->Release();
	//printf("tangdebug: ipt[%d] is allocated to vpn[%d] process %d thread %d \n", phyPageID, vpn, currentThread->spaceId, currentThread->threadId);
	//ipt[phyPageID].use = currentPageTable[vpn].use;
	//printf("DEBUG: The page I found is %d\n", phyPageID);
	return phyPageID; 

}

void PageFaultHandler(int virAddr){
	int virPageNum = virAddr/PageSize;
	int i;
	int useFlag = 0;
	//pageReplaceType = FIFO;
	
	//newPageTableEntry * currentPageTable = currentThread->space->getPageTable();
	
	//check current tlb entry valid and dirty bit
	//if valid and dirty, propagate dirty bit to ipt
/*	if((machine->tlb[tlbCounter].valid == true)&&(machine->tlb[tlbCounter].dirty == true)){
		ipt[machine->tlb[tlbCounter].physicalPage].dirty = true;
	}
*/		

/*	//step 1
	//copy content in pagetable to machine->tlb
	machine->tlb[tlbCounter].virtualPage = currentPageTable[virPageNum].virtualPage;
	machine->tlb[tlbCounter].physicalPage = currentPageTable[virPageNum].physicalPage;
	machine->tlb[tlbCounter].valid = currentPageTable[virPageNum].valid;
	machine->tlb[tlbCounter].readOnly = currentPageTable[virPageNum].readOnly;
	machine->tlb[tlbCounter].use = currentPageTable[virPageNum].use;
	machine->tlb[tlbCounter].dirty = currentPageTable[virPageNum].dirty;
	
*/	
	//for step 2
	//printf("\ntangDEBUG: the pageReplaceType is %d\n",pageReplaceType);
	iptLock->Acquire();
	for(i=0;i<NumPhysPages;i++){
		//if(ipt[i].use == true)
			//printf("tangdebug: ipt[%d] is in use with vpn[%d]\n", i, ipt[i].virtualPage);
		if((ipt[i].virtualPage == virPageNum)&&(ipt[i].valid == true)&&(ipt[i].processId == currentThread->spaceId)&&(ipt[i].use == false)){
			//ipt[i].use = true;
			break;  //Find it!
		}

	}
	
	if (i == NumPhysPages) {	//	required page not in physical memory
		
		iptLock->Release();
		useFlag = 1;
		//printf("Step2: \n!!!Can't find the corresponding missing virtual page %x for physical page %x for process with id %d", virPageNum,currentPageTable[virPageNum].physicalPage, currentThread->spaceId);
		i = IptMissHandler(virPageNum); /*For step3*/
		
    }
	//printf("DEBUG: vpn %d is allocated the ppn %i\n", virPageNum, i);
	

	//if(useFlag == 1) iptLock->Release();	
	IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts	

	//Propagate the dirty bit from tlb to ipt
	if(machine->tlb[tlbCounter].valid==true) {
		ipt[machine->tlb[tlbCounter].physicalPage].dirty = machine->tlb[tlbCounter].dirty;
	}
	
	//Populate tlb from ipt entry
	machine->tlb[tlbCounter].virtualPage = ipt[i].virtualPage;
	machine->tlb[tlbCounter].physicalPage = ipt[i].physicalPage;
	machine->tlb[tlbCounter].valid = ipt[i].valid;
	machine->tlb[tlbCounter].readOnly = ipt[i].readOnly;
	machine->tlb[tlbCounter].dirty = ipt[i].dirty;
	//machine->tlb[tlbCounter].use = ipt[i].use;
	machine->tlb[tlbCounter].use = false;
	
	tlbCounter = (tlbCounter + 1)%TLBSize;
	
    ipt[i].use = false;
	(void) interrupt->SetLevel(oldLevel);	//restore interrupts and return
	
	if(useFlag == 0) {
		iptLock->Release();
		
	}
 
}
void ExceptionHandler(ExceptionType which) {
    int type = machine->ReadRegister(2); // Which syscall?
    int rv=0; 	// the return value from a syscall

    if ( which == SyscallException ) {
	switch (type) {
	    default:
		DEBUG('a', "Unknown syscall - shutting down.\n");
	    case SC_Halt:
		DEBUG('a', "Shutdown, initiated by user program.\n");
		interrupt->Halt();
		break;
	    case SC_Create:
		DEBUG('a', "Create syscall.\n");
		Create_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
	    case SC_Open:
		DEBUG('a', "Open syscall.\n");
		rv = Open_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
		break;
	    case SC_Write:
		DEBUG('a', "Write syscall.\n");
		Write_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
		break;
	    case SC_Read:
		DEBUG('a', "Read syscall.\n");
		rv = Read_Syscall(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      machine->ReadRegister(6));
		break;
	    case SC_Close:
		DEBUG('a', "Close syscall.\n");
		Close_Syscall(machine->ReadRegister(4));
		break;
		
		//--------Project2 Part 1-----------------
		//Part 1 System Calls
		case SC_Yield:
		DEBUG('a', "Yield syscall.\n");
		//printf("myDEBUG: Yield system\n");
		currentThread->Yield();
		break;
		
		//Lock and CV
		case SC_CreateLock:
		DEBUG('a', "CreateLock syscall.\n");
		rv = CreateLock(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      currentThread->space);
		break;
		
		case SC_DestroyLock:
		DEBUG('a', "DestoryLock syscall.\n");
		DestroyLock(machine->ReadRegister(4),
			      currentThread->space);
		break;
		
		case SC_AcquireLock:
		DEBUG('a', "AcquireLock syscall.\n");
		AcquireLock(machine->ReadRegister(4),
			      currentThread->space);
		break;
		
		case SC_ReleaseLock:
		DEBUG('a', "ReleaseLock syscall.\n");
		ReleaseLock(machine->ReadRegister(4),
			      currentThread->space);
		break;
		
		case SC_CreateCondition:
		DEBUG('a', "CreateCondition syscall.\n");
		rv = CreateCondition(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      currentThread->space);
		break;
		
		case SC_DestroyCondition:
		DEBUG('a', "DestoryCondition syscall.\n");
		DestroyCondition(machine->ReadRegister(4),
			      currentThread->space);
		break;
		
		case SC_WaitCV:
		WaitCV(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      currentThread->space);
		break;
		
		case SC_SignalCV:
		SignalCV(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      currentThread->space);
		break;
		
		case SC_BroadcastCV:
		BroadcastCV(machine->ReadRegister(4),
			      machine->ReadRegister(5),
			      currentThread->space);
		break;		
		
		//--------End of Part1-----------------
		
		//------Project 2 part 2---------------
		case SC_Fork:
		Fork_Syscall(machine->ReadRegister(4));
		break;
		
		case SC_Exec:
		rv = Exec_Syscall(machine->ReadRegister(4),
						machine->ReadRegister(5));
		break;
		
		case SC_Exit:
		currentThread->Finish();
		Exit_Syscall(machine->ReadRegister(4));
		break;
		//--------Project2 Functional syscall-----------------

		case SC_Rand:
		DEBUG('a', "Rand syscall.\n");
		rv = Rand(machine->ReadRegister(4));
		break;
		
		case SC_InputInt:
		DEBUG('a', "InputInt syscall.\n");
		rv = InputInt();


		break;
		//---------End of Part2----------------
#ifdef NETWORK		
		//------Project 3 part 3---------------
		case SC_rpcCreateLock:
		DEBUG('a', "rpcCreateLock syscall.\n");
		rv = rpcCreateLock(machine->ReadRegister(4),
			      machine->ReadRegister(5),currentThread->mailbox);
		break;
		
		case SC_rpcDestroyLock:
		DEBUG('a', "rpcDestoryLock syscall.\n");
		rpcDestroyLock(machine->ReadRegister(4),currentThread->mailbox);
		break;
		
		case SC_rpcAcquireLock:
		DEBUG('a', "rpcAcquireLock syscall.\n");
		rpcAcquireLock(machine->ReadRegister(4),currentThread->mailbox);
		break;
		
		case SC_rpcReleaseLock:
		DEBUG('a', "rpcAcquireLock syscall.\n");
		rpcReleaseLock(machine->ReadRegister(4),currentThread->mailbox);
		break;

		case SC_rpcCreateMV:
		DEBUG('a', "rpcCreateMV syscall.\n");
		rv = rpcCreateMV(machine->ReadRegister(4),
			      machine->ReadRegister(5),machine->ReadRegister(6),machine->ReadRegister(7),currentThread->mailbox);
		break;
			
		case SC_rpcDestroyMV:
		DEBUG('a', "rpcDestroyMV syscall.\n");
		rpcDestroyMV(machine->ReadRegister(4),currentThread->mailbox);
		break;
		
		case SC_rpcGetValueMV:
		DEBUG('a', "rpcGetValueMV syscall.\n");
		rv = rpcGetValueMV(machine->ReadRegister(4),
			      machine->ReadRegister(5),currentThread->mailbox);
		break;

		case SC_rpcSetValueMV:
		DEBUG('a', "rpcSetValueMV syscall.\n");
		rpcSetValueMV(machine->ReadRegister(4),
			      machine->ReadRegister(5),machine->ReadRegister(6),currentThread->mailbox);
		break;
		
		case SC_rpcCreateCondition:
		DEBUG('a', "rpcCreateCondition syscall.\n");
		rv = rpcCreateCondition(machine->ReadRegister(4),
			      machine->ReadRegister(5),currentThread->mailbox);
		break;
		
		case SC_rpcDestroyCondition:
		DEBUG('a', "rpcDestroyCondition syscall.\n");
		rpcDestroyCondition(machine->ReadRegister(4),currentThread->mailbox);
		break;
		
		case SC_rpcWaitCV:
		DEBUG('a', "rpcWaitCV syscall.\n");
		rpcWaitCV(machine->ReadRegister(4),machine->ReadRegister(5),currentThread->mailbox);
		break;

		case SC_rpcSignalCV:
		DEBUG('a', "rpcSignalCV syscall.\n");
		rpcSignalCV(machine->ReadRegister(4),machine->ReadRegister(5),currentThread->mailbox);
		break;	
		
		case SC_rpcBroadcastCV:
		DEBUG('a', "rpcBroadcastCV syscall.\n");
		rpcBroadcastCV(machine->ReadRegister(4),machine->ReadRegister(5),currentThread->mailbox);
		break;	
		
#endif		
		
		//--------End of roject3 Part3-----------------
		
		
		
	}

	// Put in the return value and increment the PC
	machine->WriteRegister(2,rv);
	machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));
	machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));
	machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);
	return;
    } 
	else if(which == PageFaultException){
		PageFaultHandler(machine->ReadRegister(BadVAddrReg));
	}
	else {
      cout<<"Unexpected user mode exception - which:"<<which<<"  type:"<< type<<endl;
      interrupt->Halt();
    }
}
